#include "stdlib.h"
#include "stdio.h"
#include <cstring>
#include <map>
#include "math.h"
#include "driver.h"
#include "histogram.h"

#define MAXLINE 256

#define OutAll  1
#define SortAll 2
#define OutAve  4

/* ----------------------------------------------------------------------
   Driver, to read all files and throw data into the histogram calculator
------------------------------------------------------------------------- */
Driver::Driver(int narg, char **arg)
{
  Hists = NULL; files = NULL; fname = NULL;
  if (narg < 3) DispHelp(arg[0]);

  int nhmax = narg - 1;
  int keycol = 2, valcol = 4;
  flag = 127;

  files = new std::string[nhmax];

  // to analyse the command line options
  nhmax = 0;
  int iarg = 1;
  while (iarg < narg){
    if ( strcmp(arg[iarg], "-k") == 0 ){
      if (++iarg > narg) DispHelp(arg[0]);
      keycol = atoi(arg[iarg]);
      if (keycol < 0) DispHelp(arg[0]);

    } else if ( strcmp(arg[iarg], "-v")==0 ){
      if (++iarg > narg) DispHelp(arg[0]);
      valcol = atoi(arg[iarg]);
      if (valcol < 0) DispHelp(arg[0]);

    } else if ( strcmp(arg[iarg], "-o")==0 ){
      if (++iarg > narg) DispHelp(arg[0]);
      int n = strlen(arg[iarg]) + 1;
      if (fname) delete []fname;
      fname = new char[n];
      strcpy(fname, arg[iarg]);

    } else if ( strcmp(arg[iarg], "-a")==0 ){ // output the sum of hist instead of average
      flag &= 123;

    } else if ( strcmp(arg[iarg], "-n")==0 ){ // not sort keys
      flag &= 125;

    } else if ( strcmp(arg[iarg], "-s")==0 ){ // only output total hist
      flag &= 126;

    } else if ( strcmp(arg[iarg], "-h")==0 ){
      DispHelp(arg[0]);

    } else {
      files[nhmax].assign(arg[iarg]);
      nhmax++;

    }

    iarg++;
  }
 
  Hists = new Histogram[nhmax];
  // to read all hists
  int nhist = 0;
  for (int i=0; i<nhmax; i++){
    if (Hists[nhist].GetHist(files[i].c_str(), keycol, valcol)){
      files[nhist] = files[i];
      nhist++;
    }
  }
  if (nhist<1){DispHelp(arg[0]); exit(1);}

  // to check all hist keys
  std::map<std::string,int> Keys4All;

  for (int i=0; i<nhist; i++){
    std::map<std::string,double>::iterator it;
    for (it = Hists[i].Key2Hist.begin(); it !=  Hists[i].Key2Hist.end(); it++){
      Keys4All[it->first] = 1;
    }
  }
  int nuniq = Keys4All.size();

  // To get the sum of all hists
  double *hsum; 
  hsum = new double[nuniq];

  std::string *Index2Key;
  Index2Key = new std::string[nuniq];

  int index = 0;
  std::map<std::string,int>::iterator it;
  for (it = Keys4All.begin(); it != Keys4All.end(); it++){
    hsum[index] = 0.; 
    int nhit = 0;
    for (int i=0; i<nhist; i++){
      double value;
      nhit += Hists[i].GetValue(it->first, value);
      hsum[index] += value;
    }
    if ((flag&OutAve) && nhit > 0) hsum[index] /= double(nhit);
    Index2Key[index++] = it->first;
  }
  Keys4All.clear();
  
  // To sort the keys based on the total hist
  if (flag&SortAll){
    for (int i=0; i<nuniq-1; i++){
      for (int j=i+1; j<nuniq; j++){
        if ( hsum[j]>hsum[i] ){
          double dmid = hsum[i];
          hsum[i] = hsum[j];
          hsum[j] = dmid;
          std::string key = Index2Key[i];
          Index2Key[i] = Index2Key[j];
          Index2Key[j] = key;
        }
      }
    }
  }

  // To output joined hist
  if (fname == NULL){
    fname = new char[13];
    strcpy(fname,"hist-all.dat");
  }
  FILE *fp = fopen(fname, "w");

  // write the header
  fprintf(fp,"#index key");
  if (flag&OutAll) for (int i=0; i<nhist; i++) fprintf(fp," %s",files[i].c_str());

  if (flag&OutAve) fprintf(fp," Average\n");
  else fprintf(fp," Total\n");

  for (int ii=0; ii<nuniq; ii++){
    std::string key = Index2Key[ii];
    fprintf(fp,"%d %s", ii+1, key.c_str());
    if (flag&OutAll){
      for (int i=0; i<nhist; i++){
        double value;
        Hists[i].GetValue(key, value);
        fprintf(fp," %lg", value);
      }
    }
    fprintf(fp," %lg\n", hsum[ii]);
  }
  fclose(fp);

  delete []hsum;
  delete []Index2Key;
return;
}

/* ---------------------------------------------------------------------- */

Driver::~Driver()
{
  if (Hists) delete []Hists;
  if (files) delete []files;
  if (fname) delete []fname;
}

/* ----------------------------------------------------------------------
   To display the help info
------------------------------------------------------------------------- */

void Driver::DispHelp(const char * cmd)
{
  printf("\n%s: Program to join one or more histograms obtained by the other code, histogram.\n", cmd);
  printf("\nUsage: %s [options] file1 file2 [file3 ...]\n", cmd);
  printf("------------------------------------------------------------------------------------------------\n");
  printf("Options:\n");
  printf("       -o outfile  : to specify the output file name, by default, hist-all.dat.\n");
  printf("       -k keycol   : to specify the column number that will server as keys; by default keycol = 2.\n");
  printf("       -v valcol   : to specify the column number that will server as values; by default valcol = 4.\n");
  printf("       -n          : to tell the code not to sort the keys based on the sum of hists;\n");
  printf("       -s          : to tell the code to output only the sum/average of the hists;\n");
  printf("       -t          : to tell the code to get the total value of the hists, instead of the average;\n");
  printf("       -h          : to display this help info.\n\n");
  exit(0);
return;
}
